/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <gtest/gtest.h>
#include <mc/dbus/connection.h>
#include <mc/engine.h>
#include <mc/singleton.h>
#include <mc/string.h>
#include "mc/format.h"
#include <test_utilities/test_base.h>

#include "Endpoint.h"
#include "interface/PCIeEndpoint.h"
#include <mctp/mctp.h>

namespace {

struct test_service : public mc::engine::service {
    test_service() : mc::engine::service("bmc.kepler.mctpd") {
    }
};

class engine_test : public mc::test::TestWithEngine {
protected:
    void SetUp() override {
        service.init();
        service.start();
    }

    void TearDown() override {
        service.stop();
    }

    auto create_object(std::string_view path, mc::dict& dict) {
        auto obj = mctp_test::Endpoint::create();
        mctp_test::Endpoint::from_variant(dict, *obj);

        int         object_id = m_object_id++;
        std::string object_name = mc::fmt::format_v("%s_%d", obj->get_class_name().data(), object_id);

        obj->set_object_path(path);
        obj->set_object_name(object_name);
        obj->set_position("0101");
        service.register_object(obj);
        return obj;
    };

    auto get_managed_objects(mc::engine::abstract_object& obj) {
        mc::variants var;
        for (auto [path, _] : obj.get_managed_objects()) {
            var.push_back(path);
        };
        return var;
    };

    auto objects(std::initializer_list<std::string_view> objs) {
        mc::variants vec;
        for (auto obj : objs) {
            vec.push_back(obj);
        }
        std::sort(vec.begin(), vec.end());
        return vec;
    }

    int          m_object_id = 0;
    test_service service;
};

} // namespace

TEST_F(engine_test, test_engine_dbus_connection) {
    auto conn = mc::dbus::connection::open_session_bus(mc::get_io_context());
    conn.start();

    auto msg   = mc::dbus::message::new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                                    "org.freedesktop.DBus", "ListNames");
    auto reply = conn.send_with_reply(std::move(msg), mc::milliseconds(1000));
    ASSERT_TRUE(reply.is_valid());
    ASSERT_TRUE(!reply.is_error())
        << reply.get_error_name() << " " << mc::json::json_encode(reply.read_args());

    std::set<std::string> names;
    reply >> names;
    EXPECT_GE(names.count("bmc.kepler.mctpd"), 1);
}

bool mctp_request(mc::dbus::connection& conn, mc::dict ctx, std::vector<uint8_t>& data,
                  std::string& rsp) {
    auto msg = mc::dbus::message::new_method_call(
        "bmc.kepler.mctpd", "/bmc/kepler/Systems/1/Mctp/Endpoint/23/2",
        "bmc.kepler.Systems.Mctp.PCIeEndpoint", "Request");
    msg.set_sender("bmc.kepler.devmon");
    auto                               writer = msg.writer();
    std::map<std::string, std::string> empty_dict;
    writer << empty_dict << data << 1000;
    writer.write_variant(mc::dbus::signature_iterator("a{ss}"), ctx, 0);
    writer << empty_dict;

    auto reply = conn.send_with_reply(std::move(msg), mc::milliseconds(1000));
    if (!reply.is_valid() || !reply.is_method_return()) {
        return false;
    }
    std::vector<uint8_t> rsp_vec;
    reply >> rsp_vec;
    // 打印rsp_vec
    std::cout << "rsp_vec: " << rsp_vec.size() << std::endl;
    for (auto& byte : rsp_vec) {
        std::cout << std::hex << static_cast<int>(byte) << " ";
    }
    std::cout << std::endl;
    rsp = std::string(rsp_vec.begin(), rsp_vec.end());
    return true;
}

TEST_F(engine_test, test_add_Endpoint) {
    mc::mutable_dict EndpointDict;
    EndpointDict["bmc.kepler.Systems.Mctp.PCIeEndpoint"] =
        mc::dict({{"MessageType", 2},
                  {"ServiceName", "Network_adapter"},
                  {"Status", 1},
                  {"TargetEid", 9},
                  {"TargetPhyAddr", 23}});
    auto obj = create_object("/bmc/kepler/Systems/1/Mctp/Endpoint/23/2", EndpointDict);
    EXPECT_EQ(obj->get_object_path(), "/bmc/kepler/Systems/1/Mctp/Endpoint/23/2");
    EXPECT_EQ(obj->m_endpoint.MessageType, 2);
    EXPECT_EQ(obj->m_endpoint.ServiceName, "Network_adapter");
    EXPECT_EQ(obj->m_endpoint.Status, 1);
    EXPECT_EQ(obj->m_endpoint.TargetEid, 9);
    EXPECT_EQ(obj->m_endpoint.TargetPhyAddr, 23);

    auto conn = mc::dbus::connection::open_session_bus(mc::get_io_context());
    conn.start();
    // sleep(1000); // 等待1秒，让service启动
    std::string          rsp;
    std::vector<uint8_t> data = {};
    mc::dict             ctx  = mc::dict(
        {{"PacketType", 0}, {"ExpectRspPacketType", 128}, {"ChannelId", 0}, {"PackageId", 0}});
    bool ret = mctp_request(conn, ctx, data, rsp);
    EXPECT_TRUE(ret);
    // rsp 返回的是 ay 0 0 0 0
    EXPECT_EQ(rsp, std::string("\0\0\0\0", 4));
}
